// Copyright (95) by melonSoft Ralf Suckow Berlin, All Rights Reserved
//
// You may freely copy, distribute, and reuse the code in this example.
// Ralf Suckow disclaims any warranty of any kind, expressed or  
// implied, as to its fitness for any particular use.

#import "PlayerControl.h"
#import <strings.h>
#import <sound/accesssound.h>
#import <sound/performsound.h>

#define SAVEDFILENAME      "Library/Ortografie/words.orth"
#define CREATESAVEDFILEDIR "mkdirs ~/Library/Ortografie"

@implementation PlayerControl

- appDidInit:sender
{
  NXTypedStream * infile;
  char filename[1024];
  
  // looking up an existing file in users library
  sprintf (filename, "%s/%s", NXHomeDirectory (), SAVEDFILENAME);
  infile = NXOpenTypedStreamForFile (filename, NX_READONLY);
  
  if (!infile) {
    // creating new sound list
    sounds = [[List alloc] init];
  }
  else {
    // loading old file
    sounds = NXReadObject (infile);
    // update browser
    [browser loadColumnZero];
    NXCloseTypedStream (infile);
  }

  currentSound = nil;
  askingFor = nil;
  previousAsked = nil;
  SNDReserve(SND_ACCESS_IN | SND_ACCESS_OUT, 0);
  return self;
}

- next:sender
{
  id next;
  
  if ([sounds count] == 0) {
  
#define NEXT_WORD NXLocalizedString ("NextWord", NULL, goto next word action)
#define NO_WORDS NXLocalizedString ("NoWords", NULL, no words found problem)
                          
    NXRunAlertPanel (NEXT_WORD, NO_WORDS, NULL, NULL, NULL);
    return nil;
  }
  
  if (askingFor)
    [self playSoundNamed:"erst_loesen"];
  else {
  
    // looking for a new word to ask for
    if ([sounds count] == 1)
      next = [sounds objectAt:0];
    else
      while ((next = [sounds objectAt:random() % [sounds count]]) 
                                                          == previousAsked)
        ;
  
    askingFor = next;
    [self same:self];
  }
  return self;
}

- same:sender
{
  // ask for same work

  if (askingFor) {
    switch (random () % 3) {
      case 0: [self playSoundNamed:"schreibe_wort"];  break;
      case 1: [self playSoundNamed:"schreibe_bitte"]; break;
      case 2: [self playSoundNamed:"naechstes_wort"]; break;
    }
    [self playSound:askingFor];
    [[text setStringValue:""] selectText:self];
  }
  else
    [self playSoundNamed:"nichts_gesagt"];
    
  return self;
}

- help:sender
{   
  if (askingFor) {
    [rightText setStringValue:[askingFor name]];
    [rightPanel makeKeyAndOrderFront:self];
    [NXApp runModalFor:rightPanel];
    [rightPanel orderOut:self];
  }
  else
    [self playSoundNamed:"druecke_next"];
  
  return self;
}

- closeHelp:sender
{
  [NXApp stopModal];
  return self;
}

- ready:sender
{
  // check word
  
  if (askingFor) {
    if (!strcmp ([askingFor name], [text stringValue])) {
      switch (random () % 5) {
        case 0: [self playSoundNamed:"sehr_gut"];    break;
        case 1: [self playSoundNamed:"gut_gemacht"]; break;
        case 2: [self playSoundNamed:"einwandfrei"]; break;
        case 3: [self playSoundNamed:"otlitschno"];  break;
        case 4: [self playSoundNamed:"richtig"];     break;
      }
      if ([askingFor info])
        * (int *) [askingFor info] += 1;
      previousAsked = askingFor;
      askingFor = nil;
    }
    else {
      switch (random () % 4) {
        case 0: [self playSoundNamed:"falsch"];     break;
        case 1: [self playSoundNamed:"nochmal"];    break;
        case 2: [self playSoundNamed:"denk_nach"];  break;
        case 3: [self playSoundNamed:"noch_nicht"]; break;
      }
      if ([askingFor info])
        * (int *) [askingFor info] = 0;
    }
    [[browser loadColumnZero] display];
  }
  else
    [self playSoundNamed:"druecke_next"];
  
  return self;
}

- add:sender
{
  int     row;
  Sound * sound;
  const char * name;
  
  if (!strcmp ([newText stringValue], "")) {

#define ADD_WORD NXLocalizedString ("AddingWord", NULL, add word action)
#define NO_NAME NXLocalizedString ("TypeIn", NULL, word not typed in problem)
                          
    NXRunAlertPanel (ADD_WORD, NO_NAME, NULL, NULL, NULL);
    return nil;
  }

  // looking for existing sound
  name = [newText stringValue];
  for (row = 0; row < [sounds count]; row ++)
    if (!strcmp (name, [[sounds objectAt:row] name]))
      break;
      
  if (row != [sounds count]) { // gefunden

#define DUP_WORD NXLocalizedString ("IsInList", NULL, duplication problem)
#define AHA_ANSWER NXLocalizedString ("Indeed", NULL, a positive answer)
                          
    NXRunAlertPanel (ADD_WORD, DUP_WORD, AHA_ANSWER, NULL, NULL);
    [browser scrollColumnToVisible:row];
    return nil;
  }
  
  if (!(sound = [[Sound alloc] init])) {

#define NO_NEW_SND NXLocalizedString ("NoNewSnd", NULL, no new sound problem)
                          
    NXRunAlertPanel (ADD_WORD, NO_NEW_SND, NULL, NULL, NULL);
    return nil;
  }

  [sound setName:name];
  [sounds addObject:sound];
  [browser loadColumnZero];
  currentSound = sound;
  [[browser matrixInColumn:0] selectCellAt:row :0];
  [browser scrollColumnToVisible:row];
  [newText selectText:self];
  return self;
}

- remove:sender
{
  if (currentSound)
    [self stop:self];
  
  [[sounds removeObject:currentSound] free];
  currentSound = nil;
  [newText setStringValue:""];
  [browser loadColumnZero];
  return self;
}

- browserClicked:sender
{
  int row;
  
  row = [[browser matrixInColumn:0] selectedRow];
  currentSound = [sounds objectAt:row];
  [newText setStringValue:[currentSound name]];
  return self;
}

- play:sender
{
  if (currentSound)
    [self playSound:currentSound];

  return self;
}

- stop:sender
{
   if (currentSound /* && [recordButton state] */)
     [currentSound stop:sender];

   return self;
}

- record:sender
{ 
  // two attempts for avoiding Mach Kernel Errors

  int err;
  
  if (currentSound) {
    [recordButton setEnabled:NO];
    [playButton   setEnabled:NO];
    
    // The second recording doesn't work.
    // The structure, in fact, is emptied with deleteSamples,
    // but when you record again, a part of the
    // old sound is intermixed/overlayed with the new.
    // I don't have an idea what to do.
    
    [currentSound deleteSamples]; 
    
    if ([[currentSound setDelegate:self] record]) 
      if (err = [currentSound record]) {

#define RECORDING NXLocalizedString ("Recording", NULL, recording action)
#define SOUND_ERROR NXLocalizedString ("SndError", NULL, sound error: description)
                          
        NXRunAlertPanel (RECORDING, SOUND_ERROR, NULL, NULL, NULL,
                         SNDSoundError(err));

        [self didRecord:self];
      }
  }
  else {

#define SELECT_ENTER NXLocalizedString ("SelectOrEnter", NULL, no word selected to be recorded)
                          
    NXRunAlertPanel (RECORDING, SELECT_ENTER, NULL, NULL, NULL);
    [recordButton setState:0];
  }
  
  return self;
}

- playSound:sound
/*
**  Synchroneous Playing -- two attempts for avoiding Mach Kernel Errors
*/
{ 
  SNDSoundStruct * s;
  int err;
  
  s = [sound soundStruct];
  err = SNDStartPlaying (s, 1, 0, 0, SND_NULL_FUN, SND_NULL_FUN);
  
  if (err)
    err = SNDStartPlaying (s, 1, 0, 0, SND_NULL_FUN, SND_NULL_FUN);
   
  if (err)

#define PLAYBACK NXLocalizedString ("Playback", NULL, playback action)

    NXRunAlertPanel (PLAYBACK, SOUND_ERROR, NULL, NULL, NULL,
                     SNDSoundError(err));

  else
    SNDWait (1);
  
  return self;
}

- didRecord:sender
{
  if ([recordButton state]) {
    [[recordButton setState:0] setEnabled:YES];
    [playButton setEnabled:YES];
  }
  return self;
}

- hadError:sender
{
  int err;
  
  err = [[sender soundBeingProcessed] processingError];

#define PROBLEM NXLocalizedString ("Problem", NULL, problematic action)
                          
  NXRunAlertPanel (PROBLEM, SOUND_ERROR, NULL, NULL, NULL, 
                   SNDSoundError(err));
  [self didRecord:self];
  return self;
}

- save:sender
{
  NXTypedStream * outfile;
  char filename[1024];
 
  sprintf (filename, "%s/%s", NXHomeDirectory (), SAVEDFILENAME);
  outfile = NXOpenTypedStreamForFile (filename, NX_WRITEONLY);
  
  if (!outfile) {
    system (CREATESAVEDFILEDIR);
    outfile = NXOpenTypedStreamForFile (filename, NX_WRITEONLY);
  }
  
  if (!outfile)
  
#define SAVING NXLocalizedString ("Saving", NULL, saving action)
#define CANNOT_OPEN NXLocalizedString ("CannotOpen", NULL, cannot open: filename)
                          
    NXRunAlertPanel (SAVING, CANNOT_OPEN, NULL, NULL, NULL, filename);
  else {
    NXWriteRootObject (outfile, sounds);
    NXCloseTypedStream (outfile);
  }

  return self;
}

- playSoundNamed:(const char *)name
{
  return [self playSound:[Sound findSoundFor:name]];
}

// browser delegate methods (a very lazy delegate)

- (int)browser:sender getNumRowsInColumn:(int)column
{
  if (column == 0)
    return [sounds count];
  else
    return 0;
}

- browser:sender loadCell:cell atRow:(int)row inColumn:(int)column
{
  id   sound;
  char toDisplay[1024];
  int  * infoPtr;
  
  sound = [sounds objectAt:row];
  infoPtr = (int *) [sound info];
  sprintf (toDisplay, "%-3d  %s", infoPtr? *infoPtr : 0, [sound name]);
  
  [[[[cell setLoaded:YES] 
           setLeaf:YES]
           setStringValue:toDisplay]
           setEnabled:YES];
           
  return self;
}

@end
